package genedit.controller;

import genedit.model.graph.BreakPoint;
import genedit.model.graph.ConnectionPoint;
import genedit.model.graph.GraphElement;
import genedit.model.graph.Link;

import java.awt.geom.Point2D;
import java.awt.geom.Point2D.Double;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.swing.text.Position;

public class AlignCommand extends Command {

	/** 0 = Left, 1 = Right, 2 = Top, 3 = Bottom */
	private int direction = 0;
	private List<GraphElement> elements;
	private List<Point2D> oldPositions = new ArrayList<Point2D>();
	private HashMap<GraphElement, List<BreakPoint>> pointsBackup;
	private boolean useBackup;
	private int sum = 0;

	public AlignCommand(boolean isUndoable, int direction) {
		super(isUndoable);
		this.direction = direction;
		this.useBackup=false;
	}

	@Override
	public void execute() {
		Iterator<GraphElement> it = elements.iterator();
		if (!useBackup) {
			pointsBackup=new HashMap<GraphElement, List<BreakPoint>>();
			//backup breakpoints
			for(GraphElement element:elements){
				if(!(element instanceof Link)){
					for(ConnectionPoint connectionPoint:element.getConnectionPoints()){
						Set<Link> links=new HashSet<Link>();
						links.addAll(connectionPoint.getStarting());
						links.addAll(connectionPoint.getEnding());
						for(Link link:links){
							List<BreakPoint> backupBreakpoints=new ArrayList<BreakPoint>();
							for(BreakPoint breakPoint:link.getBreakPoints()){
								Point2D point=breakPoint.getPosition();
								backupBreakpoints.add(new BreakPoint(new Point2D.Double(point.getX(),point.getY())));
							}
							pointsBackup.put(link, backupBreakpoints);
						}
					}
				}
			}
			
			// Ovaj deo racuna poziciju prave po kojoj ce se poravnati
			while (it.hasNext()) {
				GraphElement element = it.next();
				if (element instanceof Link) {
					it.remove();
					continue;
				}

				oldPositions.add(element.getPosition());

				switch (direction) {
				case 0:
					sum += element.getPosition().getX();
					break;
				case 1:
					sum += element.getPosition().getX() + element.getSize().getWidth();
					break;
				case 2:
					sum += element.getPosition().getY();
					break;
				case 3:
					sum += element.getPosition().getY() + element.getSize().getHeight();
					break;
				}
			}

			sum /= elements.size();
		}

		it = elements.iterator();
		// Ovaj deo prepozicionira te elemente
		while (it.hasNext()) {
			GraphElement element = it.next();

			switch (direction) {
				case 0:
					alignElement(element, sum, element.getPosition().getY());
					break;
				case 1:
					alignElement(element, sum - element.getSize().getWidth(), element.getPosition()
							.getY());
					break;
				case 2:
					alignElement(element, element.getPosition().getX(), sum);
					break;
				case 3:
					alignElement(element, element.getPosition().getX(), sum
							- element.getSize().getHeight());
					break;
			}
			getDocument().getPainter(element).refreshAffine();
		}
		//ispravi linkove
		for(GraphElement element:elements){
			if(!(element instanceof Link)){
				for(ConnectionPoint connectionPoint:element.getConnectionPoints()){
					Set<Link> links=connectionPoint.getStarting();
					links.addAll(connectionPoint.getEnding());
					for(Link link:links){
						link.removeIntersectPoints();
						link.rectLink();
						link.addIntersectionPoints();
					}
				}
			}
		}
		
		getDocument().getModel().refresh();
	}

	public void undo() {
		useBackup=true;
		int n = oldPositions.size();

		for (int i = 0; i < n; i++) {
			alignElement(elements.get(i), oldPositions.get(i).getX(), oldPositions.get(i).getY());
		}

		//korisit stare breakpointe
		for(GraphElement element:elements){
			if(!(element instanceof Link)){
				for(ConnectionPoint connectionPoint:element.getConnectionPoints()){
					Set<Link> links=new HashSet<Link>();
					links.addAll(connectionPoint.getStarting());
					links.addAll(connectionPoint.getEnding());
					for(Link link:links){
						link.setBreakPoints(pointsBackup.get(link));
					}
				}
			}
		}
		
		getDocument().getModel().refresh();
	}

	private void alignElement(GraphElement element, double X, double Y) {

		Point2D oldPosition = element.getPosition();
		element.setPosition(new Point2D.Double(X, Y));
		
		Iterator<ConnectionPoint> itKonekcionih = element.getIteratorConnectionPoints();
		while (itKonekcionih.hasNext()) {
			ConnectionPoint konekciona = itKonekcionih.next();
			konekciona.setPosition(new Point2D.Double(X
					+ (konekciona.getPosition().getX() - oldPosition.getX()), Y
					+ (konekciona.getPosition().getY() - oldPosition.getY())));
		}
	}

	public List<GraphElement> getElements() {
		return elements;
	}

	public void setElements(List<GraphElement> elements) {
		this.elements = new ArrayList<GraphElement>();
		for (Iterator<GraphElement> it = elements.iterator(); it.hasNext();)
			this.elements.add(it.next());
	}

	public List<Point2D> getOldPositions() {
		return oldPositions;
	}

	public void setOldPositions(List<Point2D> oldPositions) {
		this.oldPositions = oldPositions;
	}

}
